/*
 *  BlowFish.cpp
 *  TubeTruck
 *
 *  Created by Aeturnum on 5/21/08.
 *  Copyright 2008 __MyCompanyName__. All rights reserved.
 *
 */

#include "BlowFish.h"

using namespace std;
using namespace TTP::Crypt;

void BlowFish::generateRandomKey() {
   mBlowFishKey.clear();
   srand(clock());
   for(int i = 0; i < BlowFish_KeySize; i++) {
      byte temp = rand() % 255;
      mBlowFishKey.push_back(temp);
   }
}

void BlowFish::generateRandomIv() {
   mBlowFishIv.clear();
   srand(clock());
   for(int i = 0; i < BlowFish_IvSize; i++) {
      byte temp = rand() % 255;
      mBlowFishIv.push_back(temp);
   }
}

BlowFish::BlowFish() {
   generateRandomKey();
   generateRandomIv();
}
BlowFish::BlowFish(std::vector<byte>& key, std::vector<byte>& iv) : mBlowFishKey(key), mBlowFishIv(iv)
{}
BlowFish::~BlowFish() {}
BlowFish::BlowFish(const BlowFish& o) : mBlowFishKey(o.mBlowFishKey), mBlowFishIv(o.mBlowFishIv)
{}

std::vector<byte> BlowFish::encrypt(const std::vector<byte>& data) {
   generateRandomIv();
   int retValSize = data.size() + (8-data.size()%8) + 8;
   int dataSize = data.size();
   int dataWritten;
   int extraLen = 0;
   
   EVP_CIPHER_CTX ctx;
   std::vector<byte> retVal;
   
   retVal.resize(retValSize);
   EVP_CIPHER_CTX_init(&ctx);
   EVP_EncryptInit(&ctx, 
                      EVP_bf_cbc(), // no idea how this is different from the other EVP_bf_* types, should figure it out
                      &mBlowFishKey[0], // key, not sure where we specify the length
                      &mBlowFishIv[0]); // no idea what this does
   
   if(!EVP_EncryptUpdate(&ctx, &retVal[0], &dataWritten, &data[0], dataSize)) {
      fprintf(stderr, "EVP_EncryptUpdate failed!!!\n");
   }
   if(!EVP_EncryptFinal_ex(&ctx, &retVal[dataWritten], &extraLen)) {
      fprintf(stderr, "EVP_EncryptFinal_ex failed!!!\n");
   }
   EVP_CIPHER_CTX_cleanup(&ctx);
   
   retVal.resize(dataWritten+extraLen);
   assert(dataWritten+extraLen < retValSize);
   
   return retVal;
}
std::vector<byte> BlowFish::decrypt(const std::vector<byte>& data) {
   int retValSize = data.size() + ((data.size()%8)) + 8;
   int dataLength = data.size();
   int dataWritten = 0;
   int extraLen = 0;
   
/*   cout << "decrypt:\n";
   cout << "\nEncrypt Key\n" << mBlowFishKey;
   cout << "Encrypt IV\n" << mBlowFishIv;*/
   
   EVP_CIPHER_CTX ctx;
   std::vector<byte> retVal;
   
   retVal.reserve(retValSize);
   retVal.resize(retValSize);
   EVP_CIPHER_CTX_init(&ctx);
   EVP_DecryptInit(&ctx, 
                   EVP_bf_cbc(), // see encrypt for comments 
                   &mBlowFishKey[0],
                   &mBlowFishIv[0]);
/*   cout << "\nPre-Decrypt:\n";
   cout << "data(" << data.size() << "):\n" << data;
   cout << "retValSize = " << retValSize << " dataLenth = " << dataLength << "\n";
   cout << "retVal(" << retVal.size() << "):\n" << retVal;*/
   
   if(!EVP_DecryptUpdate(&ctx, &retVal[0], &dataWritten, &data[0], dataLength)) {
      fprintf(stderr, "EVP_DecryptUpdate failed!!!\n");
      
   }
/*   cout << "\nFirst Decrypt:\n";
   cout << "retValSize = " << retValSize << " dataLenth = " << dataLength << "\n";
   cout << "extraLen = " << extraLen << " dataWritten = " << dataWritten << "\n";
   cout << "retVal(" << retVal.size() << "):\n" << retVal;*/
   
   if(!EVP_DecryptFinal_ex(&ctx, &retVal[dataWritten], &extraLen)) {
      fprintf(stderr, "EVP_DecryptFinal_ex failed!!!\n");
      ERR_load_crypto_strings();
      char errorString[400];
      ERR_error_string(ERR_get_error(), errorString);
      printf("\nBlowfish::Decrypt: %s\n", errorString);
      return retVal;
   }
   EVP_CIPHER_CTX_cleanup(&ctx);

/*   cout << "\nSecond Decrypt:\n";
   cout << "retVal(" << retVal.size() << "):\n" << retVal;
   cout << "extraLen = " << extraLen << " dataWritten = " << dataWritten << "\n";*/
   
   retVal.resize(dataWritten+extraLen);
   assert(dataWritten+extraLen < retValSize);
   
   return retVal;
}

void BlowFish::encrypt(Encryptable& data) {
   vector<byte> dataPlainText = data.getEncryptedSection();
//   cout << "\nPlainText(" << dataPlainText.size() << "):\n" << dataPlainText;
   vector<byte> retVal = encrypt(dataPlainText);
//   cout << "Encrypted Data(" << retVal.size() << "):\n" << retVal;
   data.setEncryptedData(retVal);
}
void BlowFish::decrypt(Encryptable& data) {
   vector<byte> cypherText = data.getEncryptedSection();
//   cout << "\ncypherText(" << cypherText.size() << "):\n" << cypherText;
   vector<byte> retVal = decrypt(cypherText);
//   cout << "Decrypted Data(" << retVal.size() << "):\n" << retVal;
   data.setPlainText(retVal);
}

void BlowFish::setIv(std::vector<byte>& newIv) {
   mBlowFishIv = newIv;
}

std::vector<byte> BlowFish::getIv() {
   return mBlowFishIv;
}

void BlowFish::setKey(std::vector<byte>& newKey) {
   mBlowFishKey = newKey;
}

std::vector<byte> BlowFish::getKey() {
   return mBlowFishKey;
}